package com.atom.module.mvvm.extren

import android.database.DataSetObserver
import android.view.View
import android.widget.Adapter
import android.widget.AdapterView
import androidx.annotation.CheckResult
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate

@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
fun Adapter.dataChanges() = callbackFlow<Adapter> {
    checkMainThread()
    val observer = object : DataSetObserver() {
        override fun onChanged() {
            trySend(this@dataChanges)
        }
    }
    registerDataSetObserver(observer)
    awaitClose { unregisterDataSetObserver(observer) }
}
    .conflate()
    .asInitialValueFlow { this }


@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T : Adapter> AdapterView<T>.itemClickEvents() =
    callbackFlow<AdapterViewItemClickEvent> {
        checkMainThread()
        val listener = AdapterView.OnItemClickListener { parent, view, position, id ->
            trySend(
                AdapterViewItemClickEvent(
                    view = parent,
                    clickedView = view,
                    position = position,
                    id = id
                )
            )
        }
        onItemClickListener = listener
        awaitClose { onItemClickListener = null }
    }.conflate()

public data class AdapterViewItemClickEvent(
    public val view: AdapterView<*>,
    public val clickedView: View?,
    public val position: Int,
    public val id: Long
)

/**
 *
 */
@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T : Adapter> AdapterView<T>.itemClicks() = callbackFlow<Int> {
    checkMainThread()
    val listener = AdapterView.OnItemClickListener { _, _, position, _ ->
        trySend(position)
    }
    onItemClickListener = listener
    awaitClose { onItemClickListener = null }
}.conflate()


/**
 *
 */
@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
fun <T : Adapter> AdapterView<T>.itemLongClickEvents(
    handled: (AdapterViewItemLongClickEvent) -> Boolean = { true }
) = callbackFlow<AdapterViewItemLongClickEvent> {
    checkMainThread()
    val listener = AdapterView.OnItemLongClickListener { parent, view, position, id ->
        val event = AdapterViewItemLongClickEvent(
            view = parent,
            longClickedView = view,
            position = position,
            id = id
        )
        if (handled(event)) {
            trySend(event)
            true
        } else {
            false
        }
    }
    onItemLongClickListener = listener
    awaitClose { onItemLongClickListener = null }
}.conflate()

public data class AdapterViewItemLongClickEvent(
    public val view: AdapterView<*>,
    public val longClickedView: View?,
    public val position: Int,
    public val id: Long
)

/**
 *
 */
@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
fun <T : Adapter> AdapterView<T>.itemLongClicks(
    handled: () -> Boolean = { true }
) = callbackFlow<Int> {
    checkMainThread()
    val listener = AdapterView.OnItemLongClickListener { _, _, position, _ ->
        if (handled()) {
            trySend(position)
            true
        } else {
            false
        }
    }
    onItemLongClickListener = listener
    awaitClose { onItemLongClickListener = null }
}.conflate()

/**
 *
 */
@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
fun <T : Adapter> AdapterView<T>.itemSelections() =
    callbackFlow<Int> {
        checkMainThread()
        val listener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>,
                view: View?,
                position: Int,
                id: Long
            ) {
                trySend(position)
            }

            override fun onNothingSelected(parent: AdapterView<*>) {
                trySend(AdapterView.INVALID_POSITION)
            }
        }
        onItemSelectedListener = listener
        awaitClose { onItemSelectedListener = null }
    }
        .conflate()
        .asInitialValueFlow { selectedItemPosition }

/**
 *
 */
@CheckResult
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T : Adapter> AdapterView<T>.selectionEvents() =
    callbackFlow<AdapterViewSelectionEvent> {
        checkMainThread()
        val listener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>,
                view: View?,
                position: Int,
                id: Long
            ) {
                trySend(
                    AdapterViewSelectionEvent.ItemSelected(
                        view = parent,
                        selectedView = view,
                        position = position,
                        id = id
                    )
                )
            }

            override fun onNothingSelected(parent: AdapterView<*>) {
                trySend(
                    AdapterViewSelectionEvent.NothingSelected(view = parent)
                )
            }
        }
        onItemSelectedListener = listener
        awaitClose { onItemSelectedListener = null }
    }
        .conflate()
        .asInitialValueFlow {
            val selectedPosition = selectedItemPosition
            if (selectedPosition == AdapterView.INVALID_POSITION) {
                AdapterViewSelectionEvent.NothingSelected(view = this)
            } else {
                AdapterViewSelectionEvent.ItemSelected(
                    view = this,
                    selectedView = selectedView,
                    position = selectedPosition,
                    id = selectedItemId
                )
            }
        }

sealed class AdapterViewSelectionEvent {
    abstract val view: AdapterView<*>

    data class ItemSelected(
        override val view: AdapterView<*>,
        val selectedView: View?,
        val position: Int,
        val id: Long
    ) : AdapterViewSelectionEvent()

    data class NothingSelected(override val view: AdapterView<*>) : AdapterViewSelectionEvent()
}



